package com.sojson.config.mybatis.dataScopeHandler;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;

import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
import com.sojson.base.impl.BaseUser;
import com.sojson.config.mybatis.annotation.DataScope;
import com.sojson.constant.DataScopeType;
import com.sojson.util.ExpUtil;
import com.sojson.util.StringUtil;
import com.sojson.util.param.ParamUtil;
import com.sojson.util.token.TokenUtil;

import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.schema.Column;

/**
 * Mybatis权限拦截器
 * 
 * @author liu
 * @date 2022-01-20
 */
@Component
public class PermHandler implements DataPermissionHandler {

    @Override
    public Expression getSqlSegment(Expression where, String mappedStatementId) {
        try {
            Class<?> clazz = Class.forName(mappedStatementId.substring(0, mappedStatementId.lastIndexOf(".")));
            String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(".") + 1);
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    DataScope dataScope = method.getAnnotation(DataScope.class);
                    if (dataScope != null && dataScope.isDefault()) {
                        boolean login = ParamUtil.isLogin();
                        if (!login || ParamUtil.isAdminSuper()) {
                            return where;
                        }

                        return dataScopeFilter(dataScope, where);
                    } else {
                        return where;
                    }
                }
            }
        } catch (Exception e) {
            ExpUtil.throwEx(e);
        }
        return where;
    }

    /**
     * 解析Sql
     * 
     * @param dataScope 权限注解
     * @param where     初始条件
     * @return
     * @throws IOException
     */
    public static Expression dataScopeFilter(DataScope dataScope, Expression where) throws IOException {
        Expression expression = null;
        BaseUser user = TokenUtil.getUser();
        Integer scope = TokenUtil.getUser().getDataScope();

        // 部门权限
        String deptAlias = dataScope.deptAlias();
        String deptField = dataScope.deptField();
        if (StringUtil.isNotBlank(deptField)) {
            if (DataScopeType.DATA_SCOPE_ALL == scope) {
            } else if (DataScopeType.DATA_SCOPE_CUSTOM == scope) {
                Set<String> deptIds = TokenUtil.getDepts();
                ItemsList itemsList =
                    new ExpressionList(deptIds.stream().map(StringValue::new).collect(Collectors.toList()));
                InExpression inExpression = new InExpression(buildColumn(deptAlias, deptField), itemsList); // 创建IN表达式对象，传入列名及IN范围列表
                expression =
                    ObjectUtils.isNotEmpty(expression) ? new AndExpression(expression, inExpression) : inExpression;
            } else if (DataScopeType.DATA_SCOPE_DEPT == scope) {
                EqualsTo equalsTo = new EqualsTo();
                equalsTo.setLeftExpression(buildColumn(deptAlias, deptField));
                equalsTo.setRightExpression(new LongValue(user.getDeptId()));
                expression = ObjectUtils.isNotEmpty(expression) ? new AndExpression(expression, equalsTo) : equalsTo;
            } else if (DataScopeType.DATA_SCOPE_DEPT_AND_CHILD == scope) {
                Set<String> deptIds = TokenUtil.getDepts();
                ItemsList itemsList =
                    new ExpressionList(deptIds.stream().map(StringValue::new).collect(Collectors.toList()));
                InExpression inExpression = new InExpression(buildColumn(deptAlias, deptField), itemsList); // 创建IN表达式对象，传入列名及IN范围列表
                expression =
                    ObjectUtils.isNotEmpty(expression) ? new AndExpression(expression, inExpression) : inExpression;
            }
        }

        // 个人权限
        String userField = dataScope.userField();
        if (StringUtil.isNotBlank(userField) && DataScopeType.DATA_SCOPE_SELF == scope) {
            String userAlias = dataScope.userAlias();

            String id = user.getId();
            EqualsTo equalsTo = new EqualsTo();
            equalsTo.setLeftExpression(buildColumn(userAlias, userField));
            equalsTo.setRightExpression(new LongValue(id));
            expression = ObjectUtils.isNotEmpty(expression) ? new AndExpression(expression, equalsTo) : equalsTo;
        }

        // 是否查询空值
        boolean deptIsNull = dataScope.deptIsNull();
        if (StringUtil.isNotBlank(deptField) && deptIsNull) {
            IsNullExpression isNull = new IsNullExpression();
            isNull.setLeftExpression(buildColumn(deptAlias, deptField));
            expression = ObjectUtils.isNotEmpty(expression) ? new OrExpression(expression, isNull) : isNull;
        }

        // 角色权限
        String roleField = dataScope.roleField();
        if (StringUtil.isNotBlank(roleField)) {
            String roleAlias = dataScope.roleAlias();

            Set<String> roleIds =
                dataScope.roleIsAll() ? TokenUtil.getToken().getRoleIdsExist() : TokenUtil.getRoleIds();
            ItemsList itemsList =
                new ExpressionList(roleIds.stream().map(StringValue::new).collect(Collectors.toList()));
            InExpression inExpression = new InExpression(buildColumn(roleAlias, roleField), itemsList); // 创建IN表达式对象，传入列名及IN范围列表
            expression =
                ObjectUtils.isNotEmpty(expression) ? new AndExpression(expression, inExpression) : inExpression;
        }

        return ObjectUtils.isNotEmpty(where) ? new AndExpression(where, new Parenthesis(expression)) : expression;
    }

    /**
     * 构建Column
     * 
     * @param tableAlias    表别名
     * @param columnName    字段名称
     * @return
     */
    public static Column buildColumn(String tableAlias, String columnName) {
        if (StringUtil.isNotBlank(tableAlias)) {
            columnName = tableAlias + "." + columnName;
        }

        return new Column(columnName);
    }

}